﻿using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using System.Text.Unicode;

namespace ExcelExportCS;

[JsonSerializable(typeof(int))]
[JsonSerializable(typeof(long))]
[JsonSerializable(typeof(string))]
[JsonSerializable(typeof(float))]
[JsonSerializable(typeof(double))]
[JsonSerializable(typeof(decimal))]
[JsonSerializable(typeof(JsonElement))]
[JsonSerializable(typeof(List<object>))]
[JsonSerializable(typeof(Dictionary<int, object>))]
[JsonSerializable(typeof(Dictionary<string, object>))]
[JsonSerializable(typeof(Dictionary<string, List<string>>))]
internal partial class MyJsonWriter : JsonSerializerContext
{
}

public abstract partial class ExportWriter
{
    [GeneratedRegex(@"\[(.*?)\]")]
    private static partial Regex MyRegex();

    private static readonly MyJsonWriter JsonWriter = new(new JsonSerializerOptions
    {
        WriteIndented = false,
        Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
    });

    /// <summary>
    /// 从字符串 "邮件[mail]" 中提取出 mail
    /// </summary>
    public static bool MatchExportFileName(string shortFileName, out string? exportFileName)
    {
        exportFileName = null;
        var match = MyRegex().Match(shortFileName);
        if (!match.Success) return false;
        exportFileName = match.Groups[1].Value;
        return true;
    }

    private static string FieldTypeString(FieldType type)
    {
        return type switch
        {
            FieldType.Int => "int",
            FieldType.Int64 => "long",
            FieldType.Float => "float",
            FieldType.Double => "double",
            FieldType.String or FieldType.Language => "string",
            FieldType.Array => "List<object>",
            _ => "object?"
        };
    }

    private static string MemberFieldTemplate(SheetField field, string keyUpper)
    {
        var fmt = field.Type switch
        {
            FieldType.Int or FieldType.Int64 or FieldType.Float or FieldType.Double => Template.MemberNumberFmt,
            FieldType.String or FieldType.Language => Template.MemberStringFmt,
            FieldType.Array => Template.MemberArrayFmt,
            _ => Template.MemberObjectNewFmt
        };
        return string.Format(fmt, FieldTypeString(field.Type), keyUpper, field.Desc);
    }

    /// <summary>
    /// 写json文件
    /// </summary>
    public static void WriteJsonToFile(string fileName, Dictionary<string, SheetMeta> metaDict, string clientDir, string serverDir)
    {
        if (!MatchExportFileName(fileName, out var exportFileName) || exportFileName == null)
        {
            // Console.WriteLine($"错误:文件名'{fileName}'不符合'xxx[yyy].xlsx'格式要求");
            exportFileName = Path.GetFileNameWithoutExtension(fileName);
        }
        
        var metaClientObj = new Dictionary<string, List<string>>();
        var metaServerObj = new Dictionary<string, List<string>>();
        foreach (var meta in metaDict.Values)
        {
            if (meta is { ExportClient: false, ExportServer: false })
            {
                continue;
            }

            if (meta.ExportClient)
            {
                metaClientObj[meta.FileName] = [];
            }

            if (meta.ExportServer)
            {
                metaServerObj[meta.FileName] = [];
            }

            var sortedList = meta.Fields.ToList();
            sortedList.Sort((x, y) => x.Value.CellIndex.CompareTo(y.Value.CellIndex));

            var clientElements = new List<int>();
            var serverElements = new List<int>();
            foreach (var fieldPair in sortedList)
            {
                var field = fieldPair.Value;
                if (field is { ExportClient: false, ExportServer: false })
                {
                    continue;
                }

                if (field.ExportClient)
                {
                    clientElements.Add(field.CellIndex);
                }

                if (field.ExportServer)
                {
                    serverElements.Add(field.CellIndex);
                }
            }

            if (meta.Type == SheetType.Param)
            {
                if (meta.ExportClient && clientElements.Count > 0)
                {
                    metaClientObj[meta.FileName].Add(JsonSerializer.Serialize(clientElements.Select(index => meta.SheetRowData[index]).ToList(), JsonWriter.ListObject));
                }

                if (meta.ExportServer && serverElements.Count > 0)
                {
                    metaServerObj[meta.FileName].Add(JsonSerializer.Serialize(serverElements.Select(index => meta.SheetRowData[index]).ToList(), JsonWriter.ListObject));
                }
            }
            else
            {
                foreach (var element in meta.SheetRowData)
                {
                    if (element is not List<object> rowData)
                    {
                        continue;
                    }

                    if (meta.ExportClient)
                    {
                        metaClientObj[meta.FileName].Add(JsonSerializer.Serialize(clientElements.Select(index => rowData[index]).ToList(), JsonWriter.ListObject));
                    }

                    if (meta.ExportServer)
                    {
                        metaServerObj[meta.FileName].Add(JsonSerializer.Serialize(serverElements.Select(index => rowData[index]).ToList(), JsonWriter.ListObject));
                    }
                }
            }
            meta.SheetRowData.Clear();
        }

        File.WriteAllText(Path.Combine(clientDir, exportFileName + ".json"), JsonSerializer.Serialize(metaClientObj, JsonWriter.DictionaryStringListString).Replace("\\u0022", "\\\""));
        File.WriteAllText(Path.Combine(serverDir, exportFileName + ".json"), JsonSerializer.Serialize(metaServerObj, JsonWriter.DictionaryStringListString).Replace("\\u0022", "\\\""));
    }

    /// <summary>
    /// 导出C#文件
    /// </summary>
    public static void WriteCSharpFile(string moduleName, string fileName, Dictionary<string, SheetMeta> metaDict, string clientDir, string serverDir, bool isClient)
    {
        if (!MatchExportFileName(fileName, out var exportFileName) || exportFileName == null)
        {
            // Console.WriteLine($"错误:文件名'{fileName}'不符合'xxx[yyy].xlsx'格式要求");
            exportFileName = Path.GetFileNameWithoutExtension(fileName);
        }

        List<string> clsColumns =
        [
            string.Format(Template.FileHeader, moduleName)
        ];
        List<string> loaderMemberFmt = [];
        List<string> loaderMemberReadFmt = [];
        var exportClsName = char.ToUpper(exportFileName[0]) + exportFileName[1..];
        foreach (var meta in metaDict.Values)
        {
            if (meta is { ExportClient: false, ExportServer: false })
            {
                continue;
            }

            switch (isClient)
            {
                case true when meta.ExportClient == false:
                case false when meta.ExportServer == false:
                    continue;
            }

            var clsUpperName = char.ToUpper(meta.FileName[0]) + meta.FileName[1..];
            var clsLowerName = meta.FileName;

            var sortedList = meta.Fields.ToList();
            sortedList.Sort((x, y) => x.Value.CellIndex.CompareTo(y.Value.CellIndex));

            var memberCount = 0;
            List<string> columns = [];
            List<string> readColumns = [];
            foreach (var fieldPair in sortedList)
            {
                var field = fieldPair.Value;
                if (field is { ExportClient: false, ExportServer: false })
                {
                    continue;
                }

                switch (isClient)
                {
                    case true when field.ExportClient == false:
                    case false when field.ExportServer == false:
                        continue;
                }

                var keyUpper = "Id";
                if (field.Key != meta.MainKey)
                {
                    keyUpper = char.ToUpper(field.Key[0]) + field.Key[1..];
                }

                columns.Add(MemberFieldTemplate(field, keyUpper));
                readColumns.Add(string.Format(Template.MemberReadFmt, keyUpper, FieldTypeString(field.Type), memberCount++));
            }

            if (columns.Count > 0)
            {
                var memberValue = string.Join(Environment.NewLine, columns);
                var memberReadValue = string.Join(Environment.NewLine, readColumns);
                switch (meta.Type)
                {
                    case SheetType.Param:
                        loaderMemberFmt.Add(string.Format(Template.LoaderMemberFmt, clsUpperName));
                        loaderMemberReadFmt.Add(string.Format(Template.LoaderParamReadFmt, clsUpperName, clsLowerName, exportClsName));
                        clsColumns.Add(string.Format(Template.ClassTemplateFmt, fileName, meta.SheetName, clsUpperName, memberValue, memberReadValue));
                        break;
                    case SheetType.Primary:
                        loaderMemberFmt.Add(string.Format(Template.LoaderMemberMapFmt, clsUpperName));
                        loaderMemberReadFmt.Add(string.Format(Template.LoaderClassMapReadFmt, clsUpperName, clsLowerName, exportClsName));
                        clsColumns.Add(string.Format(Template.ClassTemplateMapFmt, fileName, meta.SheetName, clsUpperName, memberValue, memberReadValue));
                        break;
                    case SheetType.Group:
                        loaderMemberFmt.Add(string.Format(Template.LoaderMemberGroupMapFmt, clsUpperName));
                        loaderMemberReadFmt.Add(string.Format(Template.LoaderClassMapReadFmt, clsUpperName, clsLowerName, exportClsName));
                        clsColumns.Add(string.Format(Template.ClassTemplateMapFmt, fileName, meta.SheetName, clsUpperName, memberValue, memberReadValue));
                        break;
                    case SheetType.None:
                    default:
                        break;
                }
            }
        }

        if (loaderMemberFmt.Count <= 0)
        {
            return;
        }

        clsColumns.Add(string.Format(Template.LoaderTemplateFmt, exportClsName, string.Join(Environment.NewLine, loaderMemberFmt), string.Join(Environment.NewLine, loaderMemberReadFmt)));
        var exportClsText = string.Join(Environment.NewLine, clsColumns);
        File.WriteAllText(isClient ? Path.Combine(clientDir, exportClsName + ".cs") : Path.Combine(serverDir, exportClsName + ".cs"), exportClsText);
    }

    /// <summary>
    /// 导出ConfigMgr.cs
    /// </summary>
    public static void WriteConfigMgrFile(string moduleName, List<string> files, string exportDir)
    {
        var memberColumns = new List<string>();
        var memberReadColumns = new List<string>();
        foreach (var file in files)
        {
            var upperName = char.ToUpper(file[0]) + file[1..];
            var lowerName = char.ToLower(file[0]) + file[1..];
            memberColumns.Add(string.Format(Template.ConfigMgrMemberFmt, upperName));
            memberReadColumns.Add(string.Format(Template.ConfigMgrReadFmt, lowerName, upperName));
        }

        var classText = string.Format(Template.ConfigMgrClassFmt, moduleName, string.Join(Environment.NewLine, memberColumns), string.Join(Environment.NewLine, memberReadColumns));
        File.WriteAllText(Path.Combine(exportDir, "ConfigMgr.cs"), classText);
    }
}